/*****************************************
 * Copyright (c) 2008 by AvePoint, Inc
 *
 * AvePoint, Inc.
 * 3 Second Street, Suite 803
 * Jersey City, NJ 07311
 * United States of America
 * Telephone: +1-201-793-1111
 * WWW: www.avepoint.com
 */
package com.myassignment.server;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import com.myassignment.util.Constants;
import com.myassignment.util.DataPacketDto;
import com.myassignment.util.XMLSAXUtility;

public class Gateway implements Constants {
    public static void main(String[] args) {
        try {
            byte[] buffer = new byte[1024*1024];
            Map<String, DataPacketDto> table = new HashMap<String, DataPacketDto>();
            DataPacketDto gateWay2 = new DataPacketDto("GateWay1", "GateWay2");
            gateWay2.setTTL(9);
            gateWay2.setDelay(30);
            gateWay2.setSourceAdress("127.0.0.1");
            gateWay2.setTargetPort(Gate_Way_2_Port_Listen);
            table.put(gateWay2.getSrcUser(), gateWay2);

            DatagramSocket datagramSocket = new DatagramSocket(Gate_Way_1_Port_Listen);
            DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length);
            ReceiveService receiver = new ReceiveService(datagramSocket, datagramPacket, table, "GateWay1");
            Thread t = new Thread(receiver);
            t.start();
            System.out.println("Gateway1 is start up......");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class ReceiveService implements Runnable, Constants {
    private DatagramSocket datagramSocket;
    private DatagramPacket datagramPacket;
    Map<String, DataPacketDto> table = new HashMap<String, DataPacketDto>();
    private String name;
    public ReceiveService(DatagramSocket datagramSocket, DatagramPacket datagramPacket, Map<String, DataPacketDto> table, String name) {
        this.datagramSocket = datagramSocket;
        this.datagramPacket = datagramPacket;
        this.table = table;
        this.name = name;
    }
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        try {
            while(true) {
                datagramSocket.receive(datagramPacket);
                String receivedData = new String(datagramPacket.getData(), 0, datagramPacket.getLength());
                List<DataPacketDto> DataPacketDtos = XMLSAXUtility.getReceivePacketInfos(receivedData);

                if (DataPacketDtos != null && DataPacketDtos.size() > 0) {
                    DataPacketDto to = DataPacketDtos.get(DataPacketDtos.size() - 1);
                    DataPacketDto stamper = new DataPacketDto(name, null);
                    stamper.setType(Request_type_forward);
                    stamper.setTTL(to.getTTL() - 1);
                    if (stamper.getTTL() < 0) {
                        System.out.println("Drop an packet, TTL < 0");
                        continue;
                    }
                    switch (to.getType()) {
                        case Request_type_forward:
                            System.out.println("Receive forward [IP:" + datagramPacket.getAddress().getHostAddress()+ ",Port:" + datagramPacket.getPort() + ", TTL: " + stamper.getTTL() + " ]");
                            String sendData = receivedData + stamper.outerXML();
                            DatagramPacket sendDataPacket = new DatagramPacket(sendData.getBytes("UTF-8"), sendData.length());
                            DataPacketDto forward = null;
                            while(true) {
                                int index = new Random().nextInt(table.size());
                                forward = (DataPacketDto) table.values().toArray()[index];
                                if (forward.getSrcUser() != null && !forward.getSrcUser().equals("Client1")) {
                                    break;
                                }
                            }
                            if (forward != null) {
                                sendDataPacket.setAddress(InetAddress.getByName(forward.getSourceAdress()));
                                sendDataPacket.setPort(forward.getTargetPort());
                                System.out.println(name + " Forward one packet to " + forward.getDstUser());
                                Thread.sleep(forward.getDelay());
                                datagramSocket.send(sendDataPacket);
                            } else {
                                System.out.println(to.getDstUser() + " is not in routing table");
                            }
                            break;
                        case Request_type_Registration:
                            System.out.println(name + ", Receive Registration:" + to.getSrcUser() + ", "+ to.getSourceAdress() +  ", TTL: " + stamper.getTTL());
                            table.put(to.getSrcUser(), to);
                            String info = "Register Successful: [" + name  + ", " +  to.getSrcUser() + ", " + to.getTargetPort()+ ", " + to.getSourceAdress() + "]";
                            DatagramPacket regDataPacket = new DatagramPacket(info.getBytes("UTF-8"), info.length());
                            regDataPacket.setAddress(InetAddress.getByName(to.getSourceAdress()));
                            regDataPacket.setPort(to.getTargetPort());
                            datagramSocket.send(regDataPacket);
                            break;
                        default:
                            System.out.println("Unknow type");
                            break;
                        }
                }
            }
        }  catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}